#include "scheduler.h"
#include "log.h"
#include "macro.h"
#include "hook.h"

namespace  sylar
{

static sylar::Logger::LoggerPtr g_plogger = SYLAR_LOG_NAME("system");

// 当前线程的调度器，同一个调度器下的所有线程共享同一个实例
static thread_local Scheduler* t_scherduler = nullptr;
//  当前线程的调度协程，每个线程都独有一份，当前线程所有的协程切换时都会切换到当前协程
static thread_local Fiber* t_Main_Scheduler_fiber = nullptr;   // Fiber* 防止增加引用计数


Scheduler* Scheduler::GetThis()
{
    return t_scherduler;
}

//  主协程 负责线程的调度
Fiber* Scheduler::GetMainFiber()
{
    return t_Main_Scheduler_fiber;
}


Scheduler::Scheduler(size_t threads, bool use_coller, const std::string& name)
    : m_name(name)
{
    SYLAR_ASSERT(threads > 0);

    if (use_coller)
    {
        sylar::Fiber::GetThis();
        --threads; // 当前线程时已经 计数了
        SYLAR_ASSERT(Scheduler::GetThis() == nullptr); // 防止这个线程在创建这个协程调度器的时候不应该有 一个协程调度器
        t_scherduler = this;
        
        /**
         * caller线程的主协程不会被线程的调度 协程run进行调度，而且，线程的调度协程停止时，应该返回caller线程的主协程
         * 在user caller情况下，把caller线程的主协程暂时保存起来，等调度协程结束时，再resume caller协程
         * 在run过程中 idle_fiber 会退出
         */
        // 主协程并不会参与到主协程的调度里面去, 做一个新的协程做调度操作
        m_rootFiber.reset(new Fiber(std::bind(&Scheduler::run, this), 0, true));

        sylar::Thread::SetName(m_name);
        t_Main_Scheduler_fiber = m_rootFiber.get();
        m_rootThreadId = sylar::GetThreadId();
        m_theadsId.push_back(m_rootThreadId);
    }
    else
    {
        m_rootThreadId = -1;
    }

    m_threaadCount = threads;

}

Scheduler::~Scheduler()
{
    SYLAR_ASSERT(m_stopping == true);

    if (Scheduler::GetThis() == this)
    {
        t_scherduler = nullptr;
    }
}

// 启动线程
void Scheduler::start()
{ 
    MutexType::Lock lock(m_mutex);
    if (!m_stopping)
    {
        return;
    }
    m_stopping = false;

    SYLAR_ASSERT(m_threads.empty());

    m_threads.resize(m_threaadCount);
    for (size_t i = 0; i < m_threaadCount; ++i)
    {   // 创建线程池
        m_threads[i].reset(new Thread(std::bind(&Scheduler::run, this), m_name + "_" + std::to_string(i)));
        m_theadsId.push_back(m_threads[i]->getId());
    }
    lock.unlock();

    // if (m_rootFiber)
    // {
    //     m_rootFiber->call();
    //     SYLAR_LOG_ERROR(g_plogger) << "call out " << m_rootFiber->getState();
    // }
}

void Scheduler::stop()
{
    m_autoStop = true;

    // SYLAR_LOG_INFO(g_plogger) << "stop m_foot_fiber state" << m_rootFiber->getState();
    if (m_rootFiber && m_threaadCount == 0 
        &&  ( m_rootFiber->getState() == Fiber::TERM 
            || m_rootFiber->getState() == Fiber::INIT))
    {
        SYLAR_LOG_INFO(g_plogger) << "this:" << this << "  stop ";
        m_stopping = true;

        if (stopping())
        {
            return;
        }
    }

    // bool exit_on_this_fiber = false;
    if (m_rootThreadId != -1)
    {
        SYLAR_ASSERT(Scheduler::GetThis() == this);
        /// .................
    }
    else
    {
        SYLAR_ASSERT(Scheduler::GetThis() != this);
        /// ................

    }

    m_stopping = true;
    for (size_t i = 0; i < m_threaadCount; ++i)
    {
        tickle();   /// 唤醒所有线程让它们执行完
    }

    if (m_rootFiber)
    {
        tickle();
    }

    if(m_rootFiber) 
    {
        // while(!stopping()) {
        //    if(m_rootFiber->getState() == Fiber::TERM
        //            || m_rootFiber->getState() == Fiber::EXCEPT) {
        //        m_rootFiber.reset(new Fiber(std::bind(&Scheduler::run, this), 0, true));
        //        SYLAR_LOG_INFO(g_plogger) << " root fiber is term, reset";
        //        t_Main_Scheduler_fiber = m_rootFiber.get();
        //    }
        //    m_rootFiber->call();
        // }
        if(!stopping()) 
        {
            m_rootFiber->call();
        }
    }

    std::vector<Thread::Ptr> thrs;
    {
        MutexType::Lock lock(m_mutex);
        thrs.swap(m_threads);
    }

    for(auto& i : thrs) 
    {
        i->join();
    }

    // if (exit_on_this_fiber)
    // {

    // }

}

void Scheduler::setThis()
{
    t_scherduler = this;
}

void Scheduler::idle()
{
    SYLAR_LOG_ERROR(g_plogger) << "idle" ;
    while (!stopping())
    {   // 没有停止 切回 MainFiber
        //SYLAR_LOG_ERROR(g_plogger) << "sylar::Fiber::YaildToHold() begin";
        sylar::Fiber::YaildToHold();
        //SYLAR_LOG_ERROR(g_plogger) << "sylar::Fiber::YaildToHold() end";
    }

}

void Scheduler::tickle()
{
    SYLAR_LOG_ERROR(g_plogger) << "tickle";
}

// 使用 use_coller 与 线程池中的线程都会调用这个
void Scheduler::run()
{
    SYLAR_LOG_ERROR(g_plogger) << "runing ";
    setThis();
    set_hook_enable(true);
    if (sylar::GetThreadId() != m_rootThreadId)
    {
        // 创建每个线程的主协程
        t_Main_Scheduler_fiber = Fiber::GetThis().get();
    }
    Fiber::ptr idle_fiber(new Fiber(std::bind(&Scheduler::idle, this)));

    Fiber::ptr cb_fiber;

    ScheduleTask ft;
    while (true)
    {
        ft.reset();
        bool tickle_me = false;
        bool is_active = false;
        {
            MutexType::Lock lock(m_mutex);
            auto it = m_fibers.begin();
            while (it != m_fibers.end())
            {
                if (it->threadID != -1 && it->threadID !=  sylar::GetThreadId())
                {
                    auto f_it=  std::find(m_theadsId.begin(), m_theadsId.end(), it->threadID);
                    if (f_it != m_theadsId.end())
                    {
                        ++it;
                        tickle_me = true;   // 通知其他协程去处理 
                        continue;
                    }
                }
                SYLAR_ASSERT(it->fiber || it->m_cb);
                // fiber 正在处理中
                if (it->fiber && it->fiber->getState() == Fiber::EXEC)
                {
                    ++it;
                    continue;
                }

                ft = *it;
                m_fibers.erase(it++);
                ++m_actiaveThreadCount;
                is_active = true;
                break;
            }
            tickle_me |= it != m_fibers.end();
        }

        if (tickle_me)
        {
            // 通知内存池中的其他线程, 这里 threadID不在线程池中 会导致一直 tickle
            tickle();
        }
        
        if (ft.fiber && (Fiber::TERM != ft.fiber->getState() &&  Fiber::EXCEPT != ft.fiber->getState()))
        {
            ft.fiber->swapIn();
            --m_actiaveThreadCount;
            // 执行中被yeild
            if (ft.fiber->getState() == Fiber::READY)
            {
                schedule(ft.fiber);
            }
            else if (Fiber::TERM != ft.fiber->getState() &&  Fiber::EXCEPT != ft.fiber->getState())
            {
                ft.fiber->m_state = Fiber::HOLD;
            }
            ft.reset();
        }
        else if (ft.m_cb)
        {
            if (cb_fiber)
            {
                cb_fiber->reset(ft.m_cb);
            }
            else
            {
                cb_fiber.reset(new Fiber(ft.m_cb, 0));
            }

            ft.reset();
            cb_fiber->swapIn();
            --m_actiaveThreadCount;
            // 执行中被yeild
            if (cb_fiber->getState() == Fiber::READY)
            {
                schedule(cb_fiber);
                cb_fiber.reset();
            }
            else if (Fiber::TERM == cb_fiber->getState() ||  Fiber::EXCEPT == cb_fiber->getState())
            {
                cb_fiber->reset(nullptr);   // 这里为什么？？ 执行完了设置一下 下次循环使用
            }
            else //if(Fiber::TERM != cb_fiber->getState())
            {
                cb_fiber->m_state = Fiber::HOLD;
                cb_fiber.reset();
            }

        }
        else
        {
            if (is_active)
            {
                --m_actiaveThreadCount;
                continue;
            }
            //SYLAR_LOG_ERROR(g_plogger) <<  idle_fiber->getState();
            if (idle_fiber->getState() == Fiber::TERM)
            {
                SYLAR_LOG_ERROR(g_plogger) << "idle fiber term";
                break;
            }
            ++m_idleThreadCount;
            // 执行空闲的一下操作
            //SYLAR_LOG_ERROR(g_plogger) << " idle_fiber->swapIn() begin";
            idle_fiber->swapIn();
           // SYLAR_LOG_ERROR(g_plogger) << " idle_fiber->swapIn() end";
            --m_idleThreadCount;
            if (idle_fiber->getState() !=  Fiber::EXCEPT && idle_fiber->getState() != Fiber::TERM)
            {
                idle_fiber->m_state = Fiber::HOLD;
            }
        }

    }
    

}

bool Scheduler::stopping()
{
    MutexType::Lock lock(m_mutex);
    return m_autoStop && m_stopping && m_fibers.empty() && m_actiaveThreadCount == 0;
}
}


